/*
 * Copyright (c) 2009, artur
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * boot/arm/early.c
 * created: Sep 30, 2009
 *
 */

#include <platformjack.h>
#include <sys/elf.h>
#include <sys/types.h>
#include <x86/platform.h>

typedef void
(*kernel_entry_t)(unsigned long magic, unsigned long addr);

#warning "Addresses of initial page dir,page and page head must be placed or defined at compile time"
extern unsigned long p_dir;
extern void *_binary_brain_start;
extern void *_binary_brain_end;
extern unsigned long _binary_brain_size;

struct hw_profile hwp;
static unsigned long *init_page_dir = (unsigned long *) &p_dir;

/* this low level init must be done
 in head.S by address independent code
 */
void
head_paging_init(void *vaddr, void *paddr, unsigned long len)
{
  unsigned long *pgt0 = (unsigned long *) ((char *) (&p_dir) + PAGE_SIZE);
  u32 address = 0;
  u32 i, j;

  /*
   * First map head
   */
  for (i = 0; i < 1024; i++)
    {
      pgt0[i] = address | 3;
      address = address + 0x1000;
    };

  /* must use phys_to_virt() on addresses */
  init_page_dir[0] = (unsigned long) pgt0;
  init_page_dir[0] = init_page_dir[0] | 3;

  for (i = 1; i < 1024; i++)
    {
      init_page_dir[i] = 0 | 2;
    };

  /* next map kernel to the same region */
  pgt0 = (unsigned long *) ((char *) pgt0 + PAGE_SIZE);
  address = 0;
  // FIXME This page offset value '0xc0000000' must be shared with kernel itself
  j = 0xc0000000 >> 22;
  for (i = 0; i < 1024; i++)
    {
      pgt0[i] = address | 3;
      address = address + 0x1000;
    };
  init_page_dir[j] = (u32) pgt0 | (u32) 3;

  set_cr3(init_page_dir);
  set_cr0(get_cr0() | CR0_PG);

}

/**
 * Load and prepare kernel elf image
 * @param img Pointer to kernel ELF image in memory
 *
 */
void *
load_kernel_img(void *img, struct multiboot_info *mbi)
{
  cpu_id_t * _cpuid;
  Elf32_Ehdr *e_hdr = NULL;
  Elf32_Phdr *p_hdr = NULL;
  Elf32_Shdr *s_hdr = NULL;
  char *dst = NULL, *src = NULL;
  size_t k = 0, n = 0;
  int i = 0;

  /* void (*k_entry)(unsigned long magic, unsigned long addr) = NULL; */
  kernel_entry_t *k_entry = NULL;

  //	if (!img){
  		img = &_binary_brain_start;
  //		/* return; */
  //	}

  head_paging_init(0, 0, 0);

  e_hdr = (Elf32_Ehdr *) img;
  p_hdr = (Elf32_Phdr *) ((vaddr_t) e_hdr + e_hdr->e_phoff);
  s_hdr = (Elf32_Shdr *) ((vaddr_t) e_hdr + e_hdr->e_shoff);
  k_entry = (kernel_entry_t *) e_hdr->e_entry;
  s_hdr = (Elf32_Shdr *) ((vaddr_t) e_hdr + e_hdr->e_shoff);

  for (i = 0; i < (int) e_hdr->e_shnum; i++, s_hdr++)
    {
      if (s_hdr->sh_addr && s_hdr->sh_size > 0)
        {
          dst = (char *) s_hdr->sh_addr;
          n = s_hdr->sh_size;
          /* for NOBITS sections
           * fill them with zeroes */
          if (s_hdr->sh_type == SHT_NOBITS)
            {
              for (k = 0; k < n; k++)
                {
                  dst[k] = 0;
                }
              continue;
            }
          else
            {
              src = (char *) ((int) img + (int) s_hdr->sh_offset);
              for (k = 0; k < n; k++)
                {
                  dst[k] = src[k];
                }
            }
        }
    }

  /*
   * Setup multiboot
   */
#warning "NO HARDCODED VALUES!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
  hwp.bootmem.type = 0;
  hwp.bootmem.start = 0;
  hwp.bootmem.size = 0x1000000;
  hwp.cmdline = "Platform Jack CMDLINE!";
  hwp.mbi = mbi;

  /* copy cpu info */
  update_cpuid();
  _cpuid = get_cpuid();
  for (i = 0; i < sizeof(cpu_id_t); i++)
    ((char *) &hwp.cpuid)[i] = ((char *) _cpuid)[i];

  return (void *) k_entry;
}

